/*******************************************************************************
 * Copyright (c) 2013 University of Luxembourg and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     @author Qin ZHANG ("Jackin" as a Global Communication ID)
 ******************************************************************************/

package lu.uni.geko.ext.ram;

import lu.uni.geko.resources.MainResourceLoader;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;

import ram.AttributeMapping;
import ram.ClassifierMapping;
import ram.Instantiation;
import ram.MappableElement;
import ram.Mapping;
import ram.OperationMapping;
import ram.ParameterMapping;

/**
 * @author Jackin
 * 
 *         This is a utility class that offers static methods for additional
 *         operations on RAM aspects which are necessary for model weaving
 *         process.
 */
public final class RamUtil
{
	
	public static void backup(final URI uri)
	{
		String fileExt = uri.fileExtension();
		if (fileExt != null)
		{
			URI backupURI = uri.trimFileExtension();
			String resultFileName = backupURI.lastSegment() + "_bak";
			backupURI = backupURI.trimSegments(1).appendSegment(resultFileName).appendFileExtension(fileExt);
			
			EObject baseRootElement = MainResourceLoader.getUniqueContentRoot(uri, "base model");
      EObject wovenRootElement = EcoreUtil.copy(baseRootElement);
      MainResourceLoader.saveEObjectAsOnlyContent(wovenRootElement, backupURI);
		} else
		{
			throw new RuntimeException("The URI '" + uri + "' has no file extension!");
		}
	}
	
	public static final void redirect(EList<Instantiation> instantiations, URI wovenMURI)
	{
		for (Instantiation instantiation : instantiations)
		{
			for (ClassifierMapping classifierMapping : instantiation.getMappings())
			{
				((InternalEObject) classifierMapping.getToElement()).eSetProxyURI(wovenMURI);

				// instantiate attribute mapping if applicable
				if (classifierMapping.getAttributeMappings() != null)
					for (AttributeMapping attributeMapping : classifierMapping.getAttributeMappings())
						((InternalEObject) attributeMapping.getToElement()).eSetProxyURI(wovenMURI);

				// instantiate operation mapping if applicable
				if (classifierMapping.getOperationMappings() != null)
					for (OperationMapping operationMapping : classifierMapping.getOperationMappings())
					{
						((InternalEObject) operationMapping.getToElement()).eSetProxyURI(wovenMURI);

						// instantiate parameter mapping if applicable
						if (operationMapping.getParameterMappings() != null)
							for (ParameterMapping parameterMapping : operationMapping.getParameterMappings())
								((InternalEObject) parameterMapping.getToElement()).eSetProxyURI(wovenMURI);
					}
			}
		}

	}
	
	public static final void instantiate(Instantiation instantiation) throws Exception
	{
		for (ClassifierMapping classifierMapping : instantiation.getMappings())
		{
			
			instantiateMapping(classifierMapping);
			
			// instantiate attribute mapping if applicable
			if (classifierMapping.getAttributeMappings() != null)
				for (AttributeMapping attributeMapping : classifierMapping.getAttributeMappings())
					instantiateMapping(attributeMapping);

			// instantiate operation mapping if applicable
			if (classifierMapping.getOperationMappings() != null)
				for (OperationMapping operationMapping : classifierMapping.getOperationMappings())
				{
					instantiateMapping(operationMapping);
					
					// instantiate parameter mapping if applicable
					if (operationMapping.getParameterMappings() != null)
						for (ParameterMapping parameterMapping : operationMapping.getParameterMappings())
							instantiateMapping(parameterMapping);
				}
		}
	}
	
	private static void instantiateMapping(Mapping mapping) throws Exception
	{
		MappableElement fromElement = mapping.getFromElement();
		MappableElement toElement = mapping.getToElement();
		
		checkIntegrity(fromElement, toElement);
		
		/*
		 * Note the order! According to RAM desirable way, the to-element is the
		 * one in the base aspect, while the from-element is that in the dependent
		 * aspect.
		 */
		fromElement.setName(toElement.getName());	
	}

	private static void checkIntegrity(MappableElement e1, MappableElement e2) throws Exception
	{
		/*
		 * Check the integrity of the two mappable elements. 
		 * 1. Two elements in the mapping must exist (completeness). No "null" entry. 
		 * 2. Two elements must be semantically equal, means sharing the same meta-class.
		 */
		// check completeness
		if (e1 == null || e2 == null)
			throw new Exception("The instantiation is not complete! " + "The from-element: " + e1 + "; the to-element: " + e2);

		/*
		// check if containing proxy
		if (e1.eIsProxy() || e2.eIsProxy())
			throw new Exception("The instantiation contains Proxy! " + "The from-element: " + e1 + "; the to-element: " + e2);
		*/
		
		// check semantically equal
		if (e1.eClass() != e2.eClass())
			throw new Exception("The instantiation contains two elements which are not semantically equal! " + "The from-element: " + e1 + "; the to-element: " + e2);
	}

}
